#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import json
import os
import random
import re
from google.appengine.api import channel
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app


class Game(db.Model):
  """All the data we store for a game."""
  userX = db.StringProperty()
  userO = db.StringProperty()
  board = db.StringProperty()
  moveX = db.BooleanProperty()
  winner = db.StringProperty()
  winning_board = db.StringProperty()


class Wins():
  x_win_patterns = [
    'XXX......',
    '...XXX...',
    '......XXX',
    'X..X..X..',
    '.X..X..X.',
    '..X..X..X',
    'X...X...X',
    '..X.X.X..',
  ]

  o_win_patterns = map(lambda s: s.replace('X', 'O'), x_win_patterns)

  x_wins = map(lambda s: re.compile(s), x_win_patterns)
  o_wins = map(lambda s: re.compile(s), o_win_patterns)


class GameUpdater():
  game = None

  def __init__(self, game):
    self.game = game

  def get_game_message(self):
    gameUpdate = {
      'board': self.game.board,
      'userX': self.game.userX,
      'userO': '' if not self.game.userO else self.game.userO,
      'moveX': self.game.moveX,
      'winner': self.game.winner,
      'winningBoard': self.game.winning_board,
    }
    game_msg_json = json.dumps(gameUpdate)
    print '========= game_msg_json:', game_msg_json
    return game_msg_json

  def send_update(self):
    print '========== send_update'
    message = self.get_game_message()
    channel.send_message(
      self.game.userX + self.game.key().id_or_name(), message)
    if self.game.userO:
      channel.send_message(
        self.game.userO + self.game.key().id_or_name(), message)

  def check_win(self):
    if self.game.moveX:
      # O just moved, check for O wins
      wins = Wins.o_wins
      potential_winner = self.game.userO
    else:
      # X just moved, check for X wins
      wins = Wins.x_wins
      potential_winner = self.game.userX

    for win in wins:
      if win.match(self.game.board):
        self.game.winner = potential_winner
        self.game.winning_board = win.pattern
        return

  def make_move(self, position, user):
    print '========== make_move:', position, ',', user
    if position >= 0 and (user == self.game.userX or user == self.game.userO):
      if self.game.moveX == (user == self.game.userX):
        boardList = list(self.game.board)
        if boardList[position] == ' ':
          boardList[position] = 'X' if self.game.moveX else 'O'
          self.game.board = "".join(boardList)
          self.game.moveX = not self.game.moveX
          self.check_win()
          self.game.put()
          self.send_update()
          return


class GameFromRequest():
  game = None;

  def __init__(self, request):
    game_key = request.get('g')
    if game_key:
      self.game = Game.get_by_key_name(game_key)

  def get_game(self):
    return self.game


class MovePage(webapp.RequestHandler):
  def post(self):
    print '---------- MovePage.post:', self.request
    game = GameFromRequest(self.request).get_game()
    user = self.request.get('u')
    if game and user:
      id = int(self.request.get('i'))
      GameUpdater(game).make_move(id, user)


class OpenedPage(webapp.RequestHandler):
  def post(self):
    print '========== OpenedPage:', self.request
    game = GameFromRequest(self.request).get_game()
    GameUpdater(game).send_update()


class MainPage(webapp.RequestHandler):
  """The main UI page, renders the 'index.html' template."""

  def get(self):
    """Renders the main page.

    When this page is shown, we create a new channel to push asynchronous
    updates to the client.
    """
    # we start from a blank game state
    user_id = self.request.get('u')
    game_key = self.request.get('g')
    print '========= user_id, game_key:', user_id, game_key
    if not user_id:
      template_values = {}
      if game_key:
        template_values['game_key'] = game_key
      path = os.path.join(os.path.dirname(__file__), 'login.html')
      self.response.out.write(template.render(path, template_values))

    game = None

    if user_id:
      if not game_key:
        game_key = str(random.randint(100, 1000))
        game = Game(key_name=game_key,
                    userX=user_id,
                    moveX=True,
                    board='         ')
        game.put()
      else:
        game = Game.get_by_key_name(game_key)
        if not game.userO:
          game.userO = user_id
          game.put()

      game_link = 'http://localhost:8080/?g=' + game_key

      if game:
        token = channel.create_channel(user_id + game_key)
        template_values = {
          'token': token,
          'me': user_id,
          'game_key': game_key,
          'game_link': game_link,
          'initial_message': GameUpdater(game).get_game_message()
        }

        path = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(path, template_values))

      else:
        self.response.out.write('No such game')

application = webapp.WSGIApplication([
                                       ('/', MainPage),
                                       ('/opened', OpenedPage),
                                       ('/move', MovePage),
                                     ], debug=True)


def main():
  run_wsgi_app(application)


if __name__ == '__main__':
  main()